﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://wpfimageviewer.codeplex.com
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using BrainTechLLC;
using ImageAppDataModels;
using ImageAppViewModels;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using BrainTechLLC.ThreadSafeObjects;
using BinaryNorthwest;

namespace ImageAppViewModels
{
    public class FileImageViewModelCollection : ObservableCollection<FileImageViewModel>
    {
        public object _lock = new object();
        protected bool _SuspendCollectionChanged;
        public ThreadSafeList<NotifyCollectionChangedEventArgs> _SavedEvents;
        protected ThreadSafeLookupNonRef<string, int> _Index;
        public List<FileImageViewModel> _List;
        public bool IndexDirty;
        public int _updatedIndexAndOrdering;
        public SortPropOrFieldAndDirection _FirstSort;
        public SortPropOrFieldAndDirection _SecondSort;
        IComparer<FileImageViewModel> _Comparer1;
        IComparer<FileImageViewModel> _Comparer2;

        public ThreadSafeLookupNonRef<string, int> Index
        {
            get
            {
                if (_Index == null || IndexDirty)
                {
                    lock (_lock)
                    {
                        if (_Index == null || IndexDirty)
                        {
                            ThreadSafeLookupNonRef<string, int> index = new ThreadSafeLookupNonRef<string, int>();
                            for (int n = 0; n < this.Count; n++)
                            {
                                var vm = this[n];
                                if (vm != null)
                                {
                                    index.Add(vm.FileName, n);
                                }
                            }
                            _Index = index;
                            IndexDirty = false;
                        }
                    }
                }

                return _Index;
            }
        }

        public bool SuspendCollectionChanged
        {
            get { return _SuspendCollectionChanged; }
            set
            {
                if (_SuspendCollectionChanged != value)
                {
                    _SuspendCollectionChanged = value;
                    if (value)
                    {
                        _SavedEvents = new ThreadSafeList<NotifyCollectionChangedEventArgs>();
                    }
                    else
                    {
                        var all = _SavedEvents.AllItems;
                        all.ForEach(o => OnCollectionChanged(o));
                        _SavedEvents = null;
                    }
                }
            }
        }

        protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            base.OnCollectionChanged(e);
        }

        public void AddItem(FileImageViewModel vm)
        {
            int thisCount = this.Count;
            if (thisCount == 0)
            {
                lock (_lock)
                {
                    thisCount = this.Count;
                    if (thisCount == 0)
                    {
                        Index.Add(vm.FileName, 0);
                        vm.SetDesiredOrderingSilent(1);
                        base.Add(vm);
                    }
                    return;
                }
            }

            if (_FirstSort == null || _FirstSort.sPropertyOrFieldName != Settings.Values.SortBy || _FirstSort.fSortDescending != Settings.Values.SortDescending)
            {
                _FirstSort = new SortPropOrFieldAndDirection(string.IsNullOrEmpty(Settings.Values.SortBy) ? "FileDateTime" : Settings.Values.SortBy, Settings.Values.SortDescending);
                _Comparer1 = _FirstSort.GetComparer<FileImageViewModel>();
            }

            if (_SecondSort == null || _SecondSort.sPropertyOrFieldName != Settings.Values.SortBy2 || _SecondSort.fSortDescending != Settings.Values.SortDescending2)
            {
                _SecondSort = new SortPropOrFieldAndDirection(string.IsNullOrEmpty(Settings.Values.SortBy2) ? "FileName" : Settings.Values.SortBy2, Settings.Values.SortDescending2);
            }

            int moveLength = Math.Max(1, (thisCount / 2) + 1);
            int location = 0;
            int direction = 1;
            int lastDirection = 0;

            //Console.WriteLine(vm.FileDateTime.ToString());
            if (_Comparer1 == null)
            {
                _Comparer1 = _FirstSort.GetComparer<FileImageViewModel>();
            }

            if (thisCount == 1)
            {
                FileImageViewModel comparison = this[0];
                int result1 = _Comparer1.Compare(vm, comparison);
                if (result1 > 0)
                {
                    lock (_lock)
                    {
                        Index.Add(vm.FileName, 1);
                        vm.SetDesiredOrderingSilent(2);
                        base.Add(vm);
                    }
                }
                else
                {
                    lock (_lock)
                    {
                        location = Index.Increment(comparison.FileName, 1);
                        Index.Add(vm.FileName, 0);
                        vm.SetDesiredOrderingSilent(1);
                        base.Insert(0, vm);
                    }
                }
                return;
            }

            while (moveLength != 0)
            {
                int newLocation = location + moveLength;
                if (newLocation >= thisCount)
                {
                    newLocation = Math.Max(0, thisCount - 1);
                }
                else if (newLocation < 0)
                {
                    newLocation = 0;
                }

                FileImageViewModel comparison = this[newLocation];
                int result1 = _Comparer1.Compare(vm, comparison);

                if (result1 == 0)
                {
                    if (_Comparer2 == null)
                    {
                        _Comparer2 = _SecondSort.GetComparer<FileImageViewModel>();
                    }
                    int result2 = _Comparer2.Compare(vm, comparison);
                    if (result2 < 0)
                    {
                        direction = -1;
                        moveLength = -1;
                        if (lastDirection == 1 || location == newLocation)
                        {
                            break;
                        }
                        location = newLocation;
                        lastDirection = direction;
                    }
                    else if (result2 > 0)
                    {
                        direction = 1;
                        moveLength = 1;
                        if (lastDirection == -1 || location == newLocation)
                        {
                            break;
                        }
                        location = newLocation;
                        lastDirection = direction;
                    }
                    else if (result2 == 0)
                    {
                        direction = 0;
                        moveLength = 0;
                        location = newLocation;
                        break;
                    }
                }
                else if (lastDirection != 0)
                {
                    break;
                }
                else if (result1 > 0 && direction == -1)
                {
                    direction = 1;
                    location = ((location + newLocation) / 2) - moveLength;
                }
                else if (result1 < 0 && direction == 1)
                {
                    direction = -1;
                    location = ((location + newLocation) / 2) - moveLength;
                }
                else if (result1 > 0 && direction == 1)
                {
                    location = newLocation;
                    moveLength = Math.Abs(moveLength / 2);
                }
                else if (result1 < 0 && direction == -1)
                {
                    location = newLocation;
                    moveLength = 0 - (Math.Abs(moveLength / 2));
                }
            }

            // maybe we should lock the whole thing?
            lock (_lock)
            {
                if (location >= thisCount)
                {
                    location = Math.Max(0, thisCount - 1);
                }
                else if (location < 0)
                {
                    location = 0;
                }
                FileImageViewModel comparison = this[location];
                int result1 = _Comparer1.Compare(vm, comparison);
                if (result1 > 0)
                {
                    while (result1 > 0 && location < thisCount - 1)
                    {
                        location++;
                        comparison = this[location];
                        result1 = _Comparer1.Compare(vm, comparison);
                    }
                }
                else if (result1 < 0)
                {
                    while (result1 < 0 && location > 0)
                    {
                        location--;
                        comparison = this[location];
                        result1 = _Comparer1.Compare(vm, comparison);
                    }
                }
                if (location == thisCount)
                {
                    int count = thisCount;

                    int desiredOrdering = count + 1;
                    //var found = this.FirstOrDefault(o => o != vm && o._DesiredOrdering + o._AddedIndexAndOrdering == desiredOrdering);
                    //if (found != null)
                    //{
                    //    Console.WriteLine("HERE");
                    //}

                    Index.Add(vm.FileName, count);
                    vm.SetDesiredOrderingSilent(desiredOrdering);
                    base.Add(vm);
                }
                else
                {
                    var insertLoc = Math.Min(thisCount - 1, Math.Max(0, location));

                    FileImageViewModel compare = this[insertLoc];
                    int compareResult = _Comparer1.Compare(vm, compare);
                    if (compareResult > 0)
                    {
                        insertLoc++;
                    }

                    int desiredOrdering = insertLoc + 1;
                    base.Insert(insertLoc, vm);
                    Index.Add(vm.FileName, insertLoc);
                    vm.SetDesiredOrderingSilent(desiredOrdering);

                    if (_List == null || _List.Count != thisCount + 1)
                    {
                        if (_List == null)
                        {
                            _List = this.ToList();
                        }
                        else
                        {
                            _List.Insert(insertLoc, vm);
                        }
                    }

                    for (int n = insertLoc + 1; n < _List.Count; n++)
                    {
                        _List[n]._AddedIndexAndOrdering++;
                        _updatedIndexAndOrdering++;
                    }

                    //_List.Sort(new Comparison<FileImageViewModel>((vm1, vm2) => (vm1._DesiredOrdering + vm1._AddedIndexAndOrdering).CompareTo((vm2._DesiredOrdering + vm2._AddedIndexAndOrdering))));
                    //for (int n = 0; n < _List.Count; n++)
                    //{
                    //    if (this[n] != _List[n])
                    //    {
                    //        Console.WriteLine("Problem");
                    //    }
                    //}
                }
                //Console.WriteLine(this.BuildList(o => o.FileDateTime.ToString()));
            }
        }

        public void UpdateIndexAndOrdering()
        {
            lock (_lock)
            {
                if (_List == null || _List.Count != this.Count)
                {
                    _List = this.ToList();
                }

                for (int n = 0; n < _List.Count; n++)
                {
                    var item = _List[n];
                    int added = Interlocked.Exchange(ref item._AddedIndexAndOrdering, 0);
                    var idx = Index.Increment(item.FileName, added);
                    int desiredOrdering = idx + 1;
                    //FileImageViewModel found = null;

                    //while ((found = _List.FirstOrDefault(o => o != item && o._DesiredOrdering + o._AddedIndexAndOrdering == desiredOrdering)) != null)
                    //{
                    //    if (_List.IndexOf(found) < _List.IndexOf(item))
                    //    {
                    //        idx = Index.Increment(item.FileName, 1);
                    //        desiredOrdering = idx + 1;
                    //    }
                    //    for (int i = n + 1; i < _List.Count; i++)
                    //    {
                    //        _List[i]._AddedIndexAndOrdering++;
                    //        _updatedIndexAndOrdering++;
                    //    }
                    //}

                    //found = this.FirstOrDefault(o => o != item && o._DesiredOrdering + o._AddedIndexAndOrdering == desiredOrdering);
                    //if (found != null)
                    //{
                    //    Console.WriteLine("HERE");
                    //}

                    item.SetDesiredOrderingSilent(desiredOrdering);
                }

                Interlocked.Exchange(ref _updatedIndexAndOrdering, 0);
            }
        }

        public void AddItemAt(FileImageViewModel vm, int idx)
        {
            lock (_lock)
            {
                Index.Add(vm.FileName, idx);
                base.Add(vm);
            }
        }

        public void RemoveItem(FileImageViewModel vm)
        {
            lock (_lock)
            {
                base.Remove(vm);
                IndexDirty = true;
            }
        }

        public int GetIndex(string fileName)
        {
            int idx;
            if (Index.TryGetValue(fileName, out idx))
            {
                return idx;
            }
            return -1;
        }
    }

}
